package cn.jiaqiang.chatgpt.data.domain.openai.service.chat;

import cn.jiaqiang.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate;
import cn.jiaqiang.chatgpt.data.domain.openai.model.entity.UserAccountEntity;
import cn.jiaqiang.chatgpt.data.domain.openai.repository.IOpenAiRepository;
import cn.jiaqiang.chatgpt.data.domain.openai.service.chat.userType.NormalUserUserChatService;
import cn.jiaqiang.chatgpt.data.domain.openai.service.chat.userType.WhiteUserUserChatService;
import cn.jiaqiang.chatgpt.data.domain.openai.service.rule.impl.account.ReduceAccount;
import cn.jiaqiang.chatgpt.data.types.common.Constants;
import cn.jiaqiang.chatgpt.data.types.common.RedisConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description 对话模型抽象类
 */
@Slf4j
@Component
public class ChatService implements IChatService {

    private final Map<Boolean, IUserTypeGroupService> userTypeGroup = new HashMap<>();

    public ChatService(WhiteUserUserChatService whiteUserChatService, NormalUserUserChatService normalUserChatService) {
        userTypeGroup.put(Boolean.TRUE, whiteUserChatService);
        userTypeGroup.put(Boolean.FALSE, normalUserChatService);
    }

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    protected IOpenAiRepository openAiRepository;

    @Resource
    private ReduceAccount reduceAccount;

    @Value("${app.config.white-list}")
    private String[] whiteList;

    @Override
    public ResponseBodyEmitter completions(ResponseBodyEmitter emitter, ChatProcessAggregate chatProcess) {
        boolean[] flags = { true, false };
        try {

            log.info("开始查询用户信息, userId = {}", chatProcess.getUserId());
            // 2. 查询用户账户信息
            UserAccountEntity userAccountEntity = openAiRepository.queryUserAccount(chatProcess.getUserId());
            if (userAccountEntity == null || userAccountEntity.getSurplusQuota() == 0) {
                // 没有次数, 视为"未开户"
                flags[0] = false;
            }

            // 从Redis中获取白名单
            String key = RedisConstants.USER_WHITE_LIST_PREFIX;
            List<String> cacheWhiteList = stringRedisTemplate.opsForList().range(key, 0, -1);
            log.info("cacheKey:{} cacheWhiteList:{}", key, cacheWhiteList);
            if (cacheWhiteList == null || cacheWhiteList.isEmpty()) {
                flags[1] = Arrays.asList(whiteList).contains(chatProcess.getUserId());
            } else {
                // redis 为空，从yml文件获取
                flags[1] = cacheWhiteList.contains(chatProcess.getUserId());
            }
            // 消息处理, value 不为空, 说明是白名单用户
            userTypeGroup.get(Boolean.TRUE.equals(flags[1])).chat(chatProcess, userAccountEntity, emitter);

            // 1. 请求应答
            emitter.onCompletion(() -> {
                        subQuota(chatProcess, flags);
                        log.info("流式问答请求完成，使用模型：{}", chatProcess.getModel());
                    }
            );
            emitter.onError(throwable -> log.error("流式问答请求疫情，使用模型：{}", chatProcess.getModel(), throwable));

        } catch (Exception e) {
            log.error("出错: ", e); //
        }
        // 5. 返回结果
        return emitter;
    }

    private void subQuota(ChatProcessAggregate chatProcess, boolean[] flags) {
        // 执行成功, 才记录用户访问次数
        log.info("记录用户 userId = {} 的访问次数 +1", chatProcess.getUserId());
        reduceAccount.recordUserCount(chatProcess.getUserId());
        // 额度扣减
        if (flags[0] && !flags[1]) {
            // 执行成功, 才扣减开户用户的额度
            log.info("扣减开户用户 userId = {} 额度", chatProcess.getUserId());
            reduceAccount.reduceUserChatCount(chatProcess.getUserId(), chatProcess.getModel());
        } else {
            log.info("用户 userId = {} '未开户', 不进行库存扣减", chatProcess.getUserId());
        }
    }

}
